package fun.parser.lexer;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PushbackReader;
import java.io.Reader;
import java.util.Arrays;

import org.lex.io.IO;

import fun.parser.LexerException;

public class Lexer {
	private static final int EOF = -1;
	private static final int BUFFER_SIZE = 8;// �ƺ�������ô��Ļ���?

	private PushbackReader reader = null;
	private int lookahead = EOF;
	// for token's push-back support
	private Token[] buf = null;
	private int pos = -1;

	public Lexer() {
		super();
		this.buf = new Token[BUFFER_SIZE];
		this.pos = -1;
	}

	public Lexer(Reader source) {
		this();
		this.setSource(source);
	}

	public void setSource(Reader source) {
		if (null != this.reader)
			IO.closeQuietly(this.reader);
		this.reader = new PushbackReader(new BufferedReader(source));
		// clear buffer
		Arrays.fill(buf, null);
		this.pos = -1;
	}

	public void pushback(Token token) {
		this.buf[++pos] = token;
	}

	public Token nextToken() throws IOException, LexerException {
		if (-1 == pos)
			return this.readNextToken();
		else
			return this.buf[pos--];
	}

	protected Token readNextToken() throws IOException, LexerException {
		lookahead = reader.read();
		// skip whitespaces
		while (Character.isWhitespace(lookahead))
			lookahead = reader.read();
		// handle END
		if (lookahead <= 0)
			return new Token("", TokenType.END, 0, 0);
		// handle operator and parentheses
		int index = DELIMITER.indexOf(lookahead);
		if (-1 != index) {
			TokenType type = DELIMITER_TOKEN_TYPE[index];
			return new Token(String.valueOf((char) lookahead), type, 0, 0);
		}
		// handle IDENTIFIER
		if (true == Character.isJavaIdentifierStart((char) lookahead)) {
			StringBuilder buffer = new StringBuilder();
			do {
				buffer.append((char) lookahead);
				lookahead = reader.read();
			} while (-1 != lookahead && false == isDelimiter(lookahead));
			if (-1 != lookahead)
				this.reader.unread(lookahead);
			return new Token(buffer.toString(), TokenType.IDENTIFIER, 0, 0);
		}
		// handle numeric value
		// ��ֵ���͵Ĵ����൱���ӣ�������ֻ��������ݴ���
		if (true == Character.isDigit(lookahead)) {
			StringBuilder buffer = new StringBuilder(16);
			do {
				buffer.append((char) lookahead);
				lookahead = reader.read();
			} while (lookahead >= 0 && false == isDelimiter(lookahead));
			if (-1 != lookahead)
				this.reader.unread(lookahead);
			return new Token(buffer.toString(), TokenType.REAL, 0, 0);
		}
		// �Ѿ�������������ˣ�������ִ�е�����Ļ���һ�������˴���
		// return null;
		if (-1 != lookahead)
			throw new LexerException("Lexer Error: '" + (char) lookahead + "' ����һ����Ч�ı�ʶ���ַ�", 0, 0);
		return null;
	}

	// delimiter�еĿո��ڷָ�id�������ֵ�ʱ������
	private static final String DELIMITER = "+-*/%^=,;() ";
	private static final TokenType[] DELIMITER_TOKEN_TYPE = { TokenType.ADD, TokenType.MINUS, TokenType.MULTIPLY, TokenType.DIVIDE, TokenType.MOD,
			TokenType.POWER, TokenType.ASSIGN, TokenType.COMMA, TokenType.SEMICOLON, TokenType.LEFT_P, TokenType.RIGHT_P };

	protected static boolean isDelimiter(int ch) {
		return Lexer.DELIMITER.indexOf(ch) != -1;
	}
}
